package scigest.configuration;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.PropertiesConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class handles multiple configuration sources 
 * <p>
 * 
 * Global Configuration:
 * 	resources/application.properties
 * 
 * <p>
 * Local Configuration:
 * 	 $HOME/.scigest/my.properties
 * 
 * 
 * 
 * @author Feiyi Wang
 *
 */
public class ScigestConfiguration {
	
	private final static Logger logger = LoggerFactory.getLogger(ScigestConfiguration.class);	

	private final static String SCIGEST_HOME = "SCIGEST_HOME";
	private final static String SITE_CONF_FILE="application.properties";
	private final static String USER_CONF_FILE="my.properties";
	private final static String CATALOG_FILE="catalog.txt";
	
	private File userConfPath;
	private File siteConfFile;
	private File userConfFile;
	
	private PropertiesConfiguration siteConfiguration;
	private PropertiesConfiguration userConfiguration;
	private CompositeConfiguration cc;
	private final ArrayList<String> keyList;
	
	private static String scigestHomeDir;

	public ScigestConfiguration() throws IOException, ConfigurationException {
		scigestHomeDir = System.getenv(SCIGEST_HOME);
		
		if (scigestHomeDir == null) {
			// now check user.home
			scigestHomeDir = System.getProperty("user.home") + "/" + "scigest";
			if (! new File(scigestHomeDir).exists()) {
				logger.error("SCIGEST_HOME is missing, quit now");
				System.exit(1);
			}
		}	
		
		logger.debug("SCIGEST_HOME is set to {}", scigestHomeDir);

		// check if the site-wide properties available
		
		siteConfFile = new File(scigestHomeDir + "/resources/" + SITE_CONF_FILE);
		userConfPath = new File(System.getProperty("user.home") + "/.scigest");
		userConfFile = new File(userConfPath, USER_CONF_FILE);
		
		if (!siteConfFile.exists()) {
			logger.error("{} is missing, quit now.", siteConfFile.getPath());
			System.exit(1);
		}
		
		
		if (!userConfPath.exists()) {
			logger.debug("{} does not exist, creating one now", userConfPath.getPath());
			userConfPath.mkdirs();
		}
		
		// create a new file if necessary
		if (! userConfFile.createNewFile()) {
			logger.debug("Detect exisiting user configurations {}", userConfFile.getPath());
		}
		this.siteConfiguration = new PropertiesConfiguration(siteConfFile);
		this.userConfiguration = new PropertiesConfiguration(userConfFile);
		this.cc = new CompositeConfiguration();
		cc.addConfiguration(this.userConfiguration);
		cc.addConfiguration(this.siteConfiguration);

		keyList = new ArrayList<String>();
		Iterator<?> it = cc.getKeys();
		while (it.hasNext()) {
			keyList.add((String) it.next());			
		}
		
	}

	/**
	 * Get the directory where user's configuration file stays
	 * 
	 * @return
	 */
	public File getUserConfPath() {
		return this.userConfPath;
	}
	
	/**
	 * 
	 * Get the full path on catalog file
	 * 
	 * @return
	 */
	public File getCatalogFile() {
		return new File(this.userConfPath, CATALOG_FILE);
	}
	
	public Configuration getSiteConfiguration() {
		return this.siteConfiguration;
	}
	
	public Configuration getUserConfiguration() {
		return this.userConfiguration;
	}
	
	public Configuration getCompositeConfiguration() {
		return this.cc;		
	}
	
	public CompositeConfiguration getCC() {
		return this.cc;
	}
	
	private URL getClassLocation() {
		// derive it from how it is invoked
		String classLocation = this.getClass().getName().replace(".", "/") + ".class";			
		URL classURL = ScigestConfiguration.class.getClassLoader().getResource(classLocation);
		return classURL;
	}
	

	public String[] getKeys() {

		String[] keyArr = new String[keyList.size()];
		
		return keyList.toArray(keyArr);
		
	}
	
	public String getString(String key) {
		return cc.getString(key);
	}
	
	public int getNumOfProps() {
		return keyList.size();
	}
	
	/**
	 * Given a key, and if the value is different from default, 
	 * this method will update the property as part of user's local settings.
	 * 
	 * @param key
	 * @param val
	 * @return
	 * @throws ConfigurationException
	 */
	public boolean setString(String key, String val) throws ConfigurationException{
		
		if (! keyList.contains(key)) {
			logger.debug("Can't locate key: [{}], setting is disallowed", key);
			return false;
		}
		
		String defaultVal = siteConfiguration.getString(key);
		if (! defaultVal.equals(val)) {
			userConfiguration.setProperty(key, val);
			userConfiguration.save();				
		}
		return true;
	}
	
	/**
	 * Restore default value
	 * @throws ConfigurationException 
	 */
	
	public void reset() throws ConfigurationException {
		userConfiguration.clear();
		userConfiguration.save();
	}
	
	public String[] getLocalKeys() {
		Iterator<?> it = userConfiguration.getKeys();
		ArrayList<String> localKeyList = new ArrayList<String>();
		while (it.hasNext()) {
			localKeyList.add((String) it.next());			
		}		
		String[] keyArr = new String[localKeyList.size()];		
		return localKeyList.toArray(keyArr);
		
	}
	
	/**
	 * Provide local property value given a local property key
	 * 
	 * @param key
	 * @return
	 */
	public String getLocalVal(String key) {
		return userConfiguration.getString(key);
	}
	
	/**
	 * Print composite configurations, for debugging purpose mostly.
	 * 
	 */
	public void dumpCompositeConfiguration() {
		
		 Iterator<?> it = cc.getKeys();
		
		 ArrayList<String> keyList = new ArrayList<String>();
		 while (it.hasNext()) {
			 keyList.add((String) it.next());
		 }
		
		System.out.println("");		
		for (String key: keyList) {
					System.out.printf("%s=%s\n", key, cc.getString(key));
		}
		System.out.println("");		

	}
	 
}
